package com.gate.conf;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;

/**
 * 网关限流配置
 * 
 * @author zgame
 *
 */
@Configuration
public class GatewayConfiguration {
	private final List<ViewResolver> viewResolvers;
	private final ServerCodecConfigurer serverCodecConfigurer;

	public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
			ServerCodecConfigurer serverCodecConfigurer) {
		this.viewResolvers = viewResolversProvider.getIfAvailable(() -> new ArrayList<ViewResolver>());
		this.serverCodecConfigurer = serverCodecConfigurer;
	}

	/**
	 * 配置SentinelGatewayBlockExceptionHandler，限流后异常处理
	 * 
	 * @return
	 */
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
		return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
	}

	@Bean
	@Order(-1)
	public GlobalFilter sentinelGatewayFilter() {
		return new SentinelGatewayFilter();
	}

	@PostConstruct
	public void doInit() {
		initGatewayRules();
	}

	/**
	 * 其中网关限流规则 GatewayFlowRule 的字段解释如下：
	 * 
	 * resource：资源名称，可以是网关中的 route 名称或者用户自定义的 API 分组名称。 resourceMode：规则是针对 API
	 * Gateway 的 route（RESOURCE_MODE_ROUTE_ID）还是用户在 Sentinel 中定义的 API
	 * 分组（RESOURCE_MODE_CUSTOM_API_NAME），默认是 route。 grade：限流指标维度，同限流规则的 grade 字段。
	 * count：限流阈值 intervalSec：统计时间窗口，单位是秒，默认是 1 秒。 controlBehavior：流量整形的控制效果，同限流规则的
	 * controlBehavior 字段，目前支持快速失败和匀速排队两种模式，默认是快速失败。 burst：应对突发请求时额外允许的请求数目。
	 * maxQueueingTimeoutMs：匀速排队模式下的最长排队时间，单位是毫秒，仅在匀速排队模式下生效。
	 * paramItem：参数限流配置。若不提供，则代表不针对参数进行限流，该网关规则将会被转换成普通流控规则；否则会转换成热点规则。其中的字段：
	 * parseStrategy：从请求中提取参数的策略，目前支持提取来源
	 * IP（PARAM_PARSE_STRATEGY_CLIENT_IP）、Host（PARAM_PARSE_STRATEGY_HOST）、任意
	 * Header（PARAM_PARSE_STRATEGY_HEADER）和任意 URL
	 * 参数（PARAM_PARSE_STRATEGY_URL_PARAM）四种模式。 fieldName：若提取策略选择 Header 模式或 URL
	 * 参数模式，则需要指定对应的 header 名称或 URL 参数名称。
	 * pattern：参数值的匹配模式，只有匹配该模式的请求属性值会纳入统计和流控；若为空则统计该请求属性的所有值。（1.6.2 版本开始支持）
	 * matchStrategy：参数值的匹配策略，目前支持精确匹配（PARAM_MATCH_STRATEGY_EXACT）、子串匹配（PARAM_MATCH_STRATEGY_CONTAINS）和正则匹配（PARAM_MATCH_STRATEGY_REGEX）。（1.6.2
	 * 版本开始支持） 用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则，或通过
	 * GatewayRuleManager.register2Property(property) 注册动态规则源动态推送（推荐方式）。

	 */

	private void initGatewayRules() {
		Set<GatewayFlowRule> rules = new HashSet<>();
		rules.add(new GatewayFlowRule("SERVICE-ONE").setCount(1) // 限流阈值
				.setIntervalSec(1) // 统计时间窗口，单位是秒，默认是 1 秒
		);
		GatewayRuleManager.loadRules(rules);
	}
}
